/**
 * 
 */
package rs.fon.rapidminer.operator.learner.tree;

import rs.fon.rapidminer.process.annotation.Parameter;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.example.Attribute;


import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import com.rapidminer.example.Attribute;
import com.rapidminer.example.Example;
import com.rapidminer.example.set.Partition;
import com.rapidminer.example.set.SimpleExampleSet;


import rs.fon.rapidminer.process.annotation.Parameter;  
import rs.fon.rapidminer.operator.learner.tree.SplittedExampleSet;
import org.apache.commons.math.stat.inference.*;


/**
 * @author Nikola
 * 
 */
public class OptimalCategoricalPossibleSplit extends AbstractPossibleSplit {
	
	@Parameter(defaultValue = "0.05", maxValue = "1.0", minValue = "0.0")
	private Double Alpha_Value;

	
	
	@Override
	 public Object DoWork(ExampleSet exampleSet, List<rs.fon.rapidminer.process.Parameter> parameters, boolean [] attributesForSplitting, double insignificanceParameter, int depth)
	    {        
	        List<SplittedExampleSet> sviSplitedi = new LinkedList<SplittedExampleSet>();
	      
	      insignificanceParameter = Double.parseDouble(parameters.get(0).getXenteredValue());
	      
	        int i=0;
	     
	       for(Attribute attribute : exampleSet.getAttributes())
	        {
	        	if (attribute.isNominal()){
	        		if(attributesForSplitting[i])
	        		{
	                   ExampleSet trainingSet = (ExampleSet)exampleSet.clone();
	                   LinkedList<SplittedExampleSet>  binarySplitList=null;
	                  try
	                  {
	                  double param = Double.parseDouble(parameters.get(0).getXenteredValue());
	                   binarySplitList = this.Split(trainingSet, attribute,insignificanceParameter, depth);   
	                  }
	                  catch(Exception ef){System.out.println("asfsdf");}
	                  if (binarySplitList!= null) 
	                	  sviSplitedi.addAll(binarySplitList);
	        	    }
	           
	        	}
	        	 i++;
	        }
	       
	        return sviSplitedi;
	    }
	   
	   
	   public Boolean greaterThenCondition( LinkedList<Double> pValues, double condition)
	   {
		  
		   Boolean returnValue = false;
		   for(int i=0; i<pValues.size();i++)
		   {
			   double value = pValues.get(i).doubleValue();
			   if (value>condition)
			   {
				   returnValue=true;
			   }
			   
		   }
		   return returnValue;
	   }
	 
	  
	   public double getMaxValue(LinkedList<Double> pValues)
	    {
	    	double maxValue = pValues.get(0).doubleValue();
	    	
	    	for(int i =0; i<pValues.size();i++)
	    	{
	    	 	if(maxValue<pValues.get(i).doubleValue())
	    	 		maxValue=pValues.get(i).doubleValue();
	    	}
	    	
	    	
	    	return maxValue;
	    }
	   
	   
	   public int getIndexOfValue(double value,LinkedList<Double> pValues )
	    {
	    	
	    	int returnIndex=0;
	    	
	    	for(int j =0; j<pValues.size();j++)
	    	{
	    	 	if(value==pValues.get(j).doubleValue())
	    	 		returnIndex = j;
	    	}
	    	return returnIndex;
	    }
	   
	   public int getIndexOfStringValue(String value,LinkedList<String> categories )
	    {
	    	
	    	int returnIndex=0;
	    	
	    	for(int j =0; j<categories.size();j++)
	    	{
	    	 	if(categories.get(j).lastIndexOf(value)!=-1)
	    	 		returnIndex = j;
	    	}
	    	return returnIndex;
	    }
	   
	    public String concatInsignificantCategories(LinkedList<Double> pValues,LinkedList<LinkedList<String>> allCombinations, double insignificanceParameter)
	    {
	    	double maxValue = getMaxValue(pValues);
	    	String multiCategory = new String();
	    	
	    	if(maxValue>insignificanceParameter)
	    	{
	    		int index = getIndexOfValue(maxValue, pValues);
	    		LinkedList<String> listOfCategories = allCombinations.get(index);
	    		
	    		for(int k=0;k<listOfCategories.size();k++)
	    		{
	    			multiCategory +=listOfCategories.get(k)+" ";
	    		}
	    		return multiCategory;    		
	    	}
	    	else
	    	
	    	return null;
	    }
	    
	    
	    
	   
	    
	    public  LinkedList<SplittedExampleSet> Split(ExampleSet exampleSet, Attribute attribute, double insignificanceParameter, int depth) {
	       
	       // boolean categoriesChange; // proverava da su kategorije spojene da bi se resetovao Generator
	        LinkedList<SplittedExampleSet> binarySplitList  =  new LinkedList<SplittedExampleSet>();
	        int[] elements = new int[exampleSet.size()];
	        for (int r =0;r<elements.length; r++)
	            elements[r] = 1;
	       
	        LinkedList<String> categories =getAllCategories(exampleSet, attribute);
	        
	        LinkedList<LinkedList<String>> allCombinations =new LinkedList();// cuva sve sve kombinacije od po dve kategorije
	        LinkedList<Double> pValues =new LinkedList<Double>();
	        boolean concatenation;
	       
	       do
	        {
	        //StringBuffer combination;
	        int index;
	        int[] indices;  //Kreira kombinacije svih kategorija i stavlja ih u levu granu
	        LinkedList<String> combination = new LinkedList<String>();
	        pValues =new LinkedList<Double>();
	        allCombinations = new LinkedList<LinkedList<String>>();
	       // for(int u=1; u<categories.size();u++){
	      
	      if (categories.size()>1)
	      {
	        CombinationGenerator x = new CombinationGenerator (categories.size(),2);
	     	        
	        while (x.hasMore ()) {
	          combination = new LinkedList<String>();
	          indices = x.getNext ();
	          for (int i = 0; i < indices.length; i++) {
	              index = indices[i];
	            combination.add(categories.get(index)); // Kreirana kombinacija od dve kategorija 
	          }
	        
	      
	      
	      
	          
	         
	          double pValue=0;
	          long [][] contigencyTable = new long [2][categories.size()] ;  
	          
	          contigencyTable=getContigencyTable(exampleSet, combination, attribute, depth);
	          ChiSquareTestImpl chiTest = new ChiSquareTestImpl();
	          try{
	          pValue = chiTest.chiSquareTest(contigencyTable);
	          }
	          catch (Exception Exept){}
	          pValues.add(new Double(pValue));   //Dodaje niz p vrednosti za svaku kombinaciju od dve kategorije
	          allCombinations.add(combination);  // Dodaje kombinaciju atributa
	          
	        }
	       
	        
	       
	       String  newCategory =   concatInsignificantCategories(pValues, allCombinations, insignificanceParameter);
	      concatenation = false;
	        
	      if (newCategory!=null) concatenation = true;
	      
	        if(concatenation){ 
	         for (int h=0; h<categories.size();h++)
	         {
	        	 if(newCategory.lastIndexOf(categories.get(h))!=-1)
	        	 {
	        		 categories.remove(h);
	        		 h--;
	        	 }
	        	 
	         }
	         categories.add(newCategory);
	        }
	          	 
	       
	        
       //  }
	      //  else concatenation=false;
	        
	      }
	      else concatenation=false;
	     //  while(greaterThenCondition( pValues, insignificanceParameter));
	        }
	          while(concatenation);
	         
	          Iterator<Example> reader = exampleSet.iterator(); // Postavljanje particije za kreiranje grane
	            int i = 0;
	            while (reader.hasNext()) {
	                Example example = reader.next();
	                String currentValue = example.getValueAsString(attribute);
	                if (inList(currentValue, categories))
	                    elements[i] = getIndexOfStringValue(currentValue, categories);
	                i++;
	               
	            }
	           
	                     
	            Partition partition = new Partition(elements, categories.size()); //Kreiranje splita
	            
	            SplittedExampleSet splitted =new SplittedExampleSet(exampleSet, partition);
	            splitted.setAttribute(attribute);
	            if(!concatenation)
	            splitted.getAttributes().remove(attribute);
	            binarySplitList.add(splitted);
	           
	            elements = new int[exampleSet.size()];
	            for (int r =0;r<elements.length; r++)
	                elements[r] = 1;
	          
	          
	       
	       
	       
	       
	      
	        return binarySplitList;
	    }
	   
	   
	    private long [][] getContigencyTable(ExampleSet exampleSet, LinkedList<String> attributeCategories, Attribute attribute, int depth)
		{
			Attribute label = exampleSet.getAttributes().getLabel();
			List<String> labelCategories = null;
			//List<String> attributeCategories = null;
			
			if(depth == 1)
			{
				SimpleExampleSet workingExampleSet = (SimpleExampleSet) exampleSet;
				labelCategories = getAllCategories(workingExampleSet,label);
				//attributeCategories = workingExampleSet.getAllCategories(attribute);
			}
			else 
			{ 
				SplittedExampleSet workingExampleSet = (SplittedExampleSet)exampleSet;
				labelCategories = workingExampleSet.getAllCategories(label);
				//attributeCategories = workingExampleSet.getAllCategories(attribute);
			}		
			
			int numberOfLabelCategories = labelCategories.size();
			//int numberOfAttributeCategories = attributeCategories.size();
			
			long[][] matrix = new long[2][numberOfLabelCategories];
			
			for(int i = 0; i < 2; i++)
				for(int j = 0; j < numberOfLabelCategories; j++)
				{
					matrix[i][j] = 0;
					
					for(Example example : exampleSet)
					{
					      if(labelCategories.get(j).equals(example.getNominalValue(label)) &&    (attributeCategories.get(i).lastIndexOf(example.getNominalValue(attribute))!=-1)) 
					    		  matrix[i][j] += 1;
					}
				}
			
			return matrix;
		}
	    
	    public Object GetBestAttribute(SplittedExampleSet exampleSet)
		{
			return null;
		}
		
		public Object GetBenefit(SplittedExampleSet exampleSet)
		{
			return null;
		}
	
		  public  LinkedList<String> getAllCategories(ExampleSet exampleSet, Attribute attribute) {
		        LinkedList<String> allCategoryList  =  new LinkedList<String>();
		   
		       
		        Iterator<Example> reader = exampleSet.iterator();
		       
		        while (reader.hasNext()) {
		            Example example = reader.next();
		            String currentValue = example.getValueAsString(attribute);
		            if (!inList(currentValue,allCategoryList))
		                allCategoryList.add(currentValue);
		        }
		       
		        //return new SplittedExampleSet(exampleSet, partition);
		        return allCategoryList;
		    }
		   
		    public  boolean inList(String category,List<String> allCategoryList) {
		       
		        for(int i =0; i<allCategoryList.size();i++)
		        {
		            if (allCategoryList.get(i).equals(category))
		                return true;
		         
		        }
		       
		       return false;
		    }
}
